1 /*
2 Copyright: Marcelo S. N. Mancini (Hipreme|MrcSnm), 2018 - 2021
3 License:   [https://creativecommons.org/licenses/by/4.0/|CC BY-4.0 License].
4 Authors: Marcelo S. N. Mancini
5 
6 	Copyright Marcelo S. N. Mancini 2018 - 2021.
7 Distributed under the CC BY-4.0 License.
8    (See accompanying file LICENSE.txt or copy at
9 	https://creativecommons.org/licenses/by/4.0/
10 */
11 module hip.error.handler;
12 import hip.console.log;
13 import hip.util.conv;
14 
15 /** 
16  * Base clas for documenting errors
17  */
18 
19 public class EngineErrorStack
20 {
21     public string stackName;
22     public string[] errorStack;
23 
24     private this(string stackName)
25     {
26         this.stackName = stackName;
27     }
28 
29     public static EngineErrorStack getNewStack(string stackName)
30     {
31         return new EngineErrorStack(stackName);
32     }
33     /** 
34      * Adds an error to the stack
35      * Params:
36      *   errorHeader = Error Short
37      *   errorMessage = Error Long
38      */
39     public void addError(string errorHeader, string errorMessage)
40     {
41         errorStack~= errorHeader ~ ": " ~ errorMessage;
42     }
43 
44     public void showStack()
45     {
46         rawerror("ErrorStack: " ~ stackName);
47         const int len = cast(int)this.errorStack.length;
48         for(int i = 0; i < len; i++)
49             rawerror("\t" ~ errorStack[i]);
50     }
51 }
52 
53 
54 import core.stdc.stdlib;
55 
56 version(iOS) 
57 {
58     extern(C) void terminateiOSApp(int code);
59     ///iOS has a special terminate function which ought to be called when something happens.
60     alias terminate = terminateiOSApp;
61 }
62 else
63 {
64     alias terminate = core.stdc.stdlib.exit;
65 }
66 
67 /** 
68  * Class Used for handling errors
69  */
70 public static class ErrorHandler
71 {
72     __gshared
73     {
74         private  bool HAS_ANY_ERROR_HAPPENNED = false;
75         private  EngineErrorStack currentStack;
76         private  EngineErrorStack[] stackHistory;
77         private  string[] warnHistory;
78         private  bool isListening = false;
79         public  string LAST_ERROR = "";
80     }
81     
82     /** 
83      * This function will look wether any error has happenned
84      *  stackName = This will help identify where the error ocurred
85      */
86     public static void startListeningForErrors(string stackName = "Default Error")
87     {
88         if(isListening)
89             stopListeningForErrors();
90         HAS_ANY_ERROR_HAPPENNED = false;
91         isListening = true;
92         currentStack = EngineErrorStack.getNewStack(stackName);
93     }
94     /** 
95      * Will stop listening and 
96      * Returns: HAS_ANY_ERROR_HAPPENNED
97      */
98     public static bool stopListeningForErrors()
99     {
100         if(HAS_ANY_ERROR_HAPPENNED)
101             stackHistory~= currentStack;
102         currentStack = null;
103         isListening = false;
104         return HAS_ANY_ERROR_HAPPENNED;
105     }
106 
107     private static void getError(lazy string errorHeader, lazy string error)
108     {
109         if(isListening)
110         {
111             LAST_ERROR = errorHeader ~ ": \n" ~ error;
112             currentStack.addError(errorHeader, error);
113         }
114     }
115 
116 
117     /** 
118      * This function adds to the error stack
119      * Params:
120      *   errorTitle = Error Header
121      *   errorMessage = Error Message
122      */
123     public static void showErrorMessage(string errorTitle, string errorMessage, bool isFatal = false)
124     {
125         if(isFatal)
126         {
127             rawfatal(errorTitle, "\t[[", errorMessage, "]]");
128         }
129         else
130         {
131             rawerror(errorTitle, "\t[[", errorMessage, "]]");
132         }
133         getError(errorTitle, errorMessage);
134     }
135     public static void showWarningMessage(string warningTitle, string warningMessage)
136     {
137         rawwarn("\nWarning: " ~ warningTitle);
138         rawwarn(warningMessage);
139         warnHistory~= warningTitle~": "~warningMessage;
140     }
141 
142     /** 
143      * 
144      * Params:
145      *   expression = Expression for looking wether an error has happenned
146      *   errorTitle = Error Header
147      *   errorMessage = Error Message
148      * Returns: If the error happenned
149      */
150     public static bool assertErrorMessage(bool expression, string errorTitle, string errorMessage, bool isFatal = false,
151     string file = __FILE__, size_t line =__LINE__, string mod = __MODULE__, string func = __PRETTY_FUNCTION__)
152     {
153         expression = !expression; //Negate the expression, as it must return wether error ocurred
154         if(expression)
155         {
156             version(HIPREME_DEBUG)
157             {
158                 string where = "at module '"~mod~"' "~file~":"~to!string(line)~"("~func~")\n\t";
159             }
160             else{string where="";}
161             showErrorMessage(where~errorTitle, errorMessage, isFatal);
162         }
163         return expression;
164     }
165 
166     /** 
167      * 
168      * Params:
169      *   expression = Expression for looking wether an error has happenned
170      *   errorTitle = Error Header
171      *   errorMessage = Error Message
172      * Returns: If the error happenned
173      */
174     public static bool assertLazyErrorMessage(bool expression, lazy string errorTitle, lazy string errorMessage, bool isFatal = false,
175     string file = __FILE__, size_t line =__LINE__, string mod = __MODULE__, string func = __PRETTY_FUNCTION__)
176     {
177         expression = !expression; //Negate the expression, as it must return wether error ocurred
178         if(expression)
179         {
180             version(HIPREME_DEBUG)
181             {
182                 string where = "at module '"~mod~"' "~file~":"~to!string(line)~"("~func~")\n\t";
183             }
184             else{string where="";}
185             showErrorMessage(where~errorTitle, errorMessage, isFatal);
186         }
187         return expression;
188     }
189 
190     /**
191     *   If you're running on a loop or need to concat your failure message, prefer using assertLazyExit.
192     */
193     public static void assertExit(bool expression, string onAssertionFailure = "Assertion Failure",
194     string file = __FILE__, size_t line = __LINE__, string mod = __MODULE__, string func = __PRETTY_FUNCTION__)
195     {
196         if(!expression)
197         {
198             cast(void)ErrorHandler.assertErrorMessage(false, "HipAssertion", onAssertionFailure, true,
199             file, line, mod, func);
200             terminate(EXIT_FAILURE);
201         }
202     }
203 
204     public static void assertLazyExit(bool expression, lazy string onAssertionFailure,
205     string file = __FILE__, size_t line = __LINE__, string mod = __MODULE__, string func = __PRETTY_FUNCTION__)
206     {
207         if(!expression)
208         {
209             cast(void)ErrorHandler.assertLazyErrorMessage(false, "HipAssertion", onAssertionFailure, true,
210             file, line, mod, func);
211             terminate(EXIT_FAILURE);
212         }
213     }
214 
215     static immutable(string) assertReturn(string expression)(string onAssertionFailureMessage)
216     {
217         return `if(ErrorHandler.assertErrorMessage(`~expression~`, "HipAssertion", "`~onAssertionFailureMessage~
218         `"))return;`;
219     }
220 
221     public static void showEveryError()
222     {
223         foreach(stack; stackHistory)
224         {
225             stack.showStack();
226         }
227     }
228 }